/*
 * 版权所有 (c) 2022-2099 TBIT。保留所有权利。
 */

package com.tbit.uqbike.service.business.impl;

import cn.hutool.core.collection.CollUtil;
import cn.hutool.core.collection.ListUtil;
import cn.hutool.core.util.StrUtil;
import com.github.pagehelper.PageHelper;
import com.github.pagehelper.PageInfo;
import com.tbit.common.constant.MannedStatusEnum;
import com.tbit.uqbike.constant.ControlTypeConstant;
import com.tbit.uqbike.constant.MachineAbnormalConstant;
import com.tbit.uqbike.manager.exception.BaseException;
import com.tbit.uqbike.object.pojo.Machine;
import com.tbit.uqbike.object.pojo.Manned;
import com.tbit.uqbike.object.pojo.bo.MannedBatchBindBO;
import com.tbit.uqbike.object.pojo.bo.MannedBindingBO;
import com.tbit.uqbike.object.pojo.vo.MannedBindingRespVO;
import com.tbit.uqbike.object.pojo.vo.*;
import com.tbit.uqbike.object.value.LoginInfo;
import com.tbit.uqbike.service.base.MachineService;
import com.tbit.uqbike.service.base.TokenService;
import com.tbit.uqbike.service.business.MannedService;
import com.tbit.uqbike.service.business.TerControlService;
import com.tbit.uqbike.service.rpcservice.impl.MachineAbnormalServiceImpl;
import com.tbit.uqbike.webmanager.dao.core.MannedDao;
import com.tbit.uqbike.webmanager.util.StringUtil;
import com.tbit.uqbike.webmanager.util.excel.ExcelUtil;
import org.springframework.stereotype.Service;

import javax.annotation.Resource;
import java.util.*;
import java.util.function.Function;
import java.util.stream.Collectors;

/**
 * @author <a href="mailto:li.yao@tbit.com.cn">yao</a>
 * @version 4.2.0
 * @since 2024/9/10 11:19
 */
@Service
public class MannedServiceImpl implements MannedService {

    @Resource
    private MannedDao mannedDao;

    @Resource
    private MachineService machineService;

    @Resource
    private TokenService tokenService;

    @Resource
    private TerControlService terControlService;

    @Resource
    private MachineAbnormalServiceImpl machineAbnormalService;

    @Override
    @SuppressWarnings(value = "resource")
    public PageInfo<MannedRespVO> getPage(MannedPageReqVO reqVO) {
        return PageHelper.startPage(reqVO.getPageNO(), reqVO.getRowCount())
                .doSelectPageInfo(() -> mannedDao.selectList(reqVO));
    }

    @Override
    public List<MannedRespVO> getList(MannedPageReqVO reqVO) {
        return mannedDao.selectList(reqVO);
    }

    @Override
    public List<Manned> getByMannedNos(List<String> mannedNos) {
        return CollUtil.isEmpty(mannedNos) ? Collections.emptyList() : mannedDao.selectByMannedNos(mannedNos);
    }

    @Override
    public List<Manned> getByMachineIds(List<Integer> machineIds) {
        return CollUtil.isEmpty(machineIds) ? Collections.emptyList() : mannedDao.selectByMachineIds(machineIds);
    }

    @Override
    public void binding(MannedBindingReqVO reqVO) {
        Machine machine = machineService.getByMachineId(reqVO.getMachineId());
        if (machine == null) {
            throw new BaseException("machine_not_exist");
        }

        LoginInfo loginInfo = tokenService.getLoginInfo();

        MannedBindingExcelVO mannedBinding = new MannedBindingExcelVO(machine.getUserCode(), reqVO.getMannedNO());
        MannedBatchBindBO bo = MannedBatchBindBO.of(reqVO.getAccountId(), loginInfo, mannedBinding);

        batchBind(bo).stream().findFirst().ifPresent(b -> {
            throw new BaseException(b.getReason());
        });
    }

    private List<MannedBindingRespVO> batchBind(MannedBatchBindBO bo) {
        Date now = new Date();
        List<String> userCodes = bo.getUserCodes();
        List<String> mannedNos = bo.getMannedNos();

        List<MannedBindingRespVO> result = new ArrayList<>();
        if (CollUtil.isEmpty(userCodes)) {
            for (MannedBindingBO mannedBindingBO : bo.getMannedBindingList()) {
                MannedBindingExcelVO mannedBinding = mannedBindingBO.getMannedBinding();
                result.add(new MannedBindingRespVO(mannedBinding.getUserCode(), mannedBinding.getMannedNo(), "车辆编号不能为空"));
            }
            return result;
        }

        Map<String, Machine> machineMap = machineService.getByAccountIdAndUserCodes(bo.getAccountId(), userCodes)
                .stream().collect(Collectors.toMap(Machine::getUserCode, Function.identity()));
        List<Integer> machineIds = machineMap.values().stream().map(Machine::getMachineId).collect(Collectors.toList());

        Map<Integer, Manned> machineIdMannedMap = getByMachineIds(machineIds)
                .stream().collect(Collectors.toMap(Manned::getMachineId, Function.identity()));
        Map<String, Manned> mannedMap = getByMannedNos(mannedNos)
                .stream().collect(Collectors.toMap(Manned::getMannedNO, Function.identity(), (s1, s2) ->
                        s1.getUpdateTime().after(s2.getUpdateTime()) ? s1 : s2));

        List<Manned> mannedList = new ArrayList<>();
        for (MannedBindingBO mannedBinding : bo.getMannedBindingList()) {
            String userCode = mannedBinding.getMannedBinding().getUserCode();
            String mannedNo = mannedBinding.getMannedBinding().getMannedNo();
            Machine machine = machineMap.get(userCode);

            if (check(userCode, mannedNo, machine, mannedMap, machineIdMannedMap, result)) {
                continue;
            }

            Manned newManned = createManned(bo, machine, mannedNo, now, machineIdMannedMap);
            mannedList.add(newManned);
            mannedBinding.setManned(newManned);
            mannedBinding.setMachine(machine);
            machineIdMannedMap.put(newManned.getMannedId(), newManned);
            mannedMap.put(newManned.getMannedNO(), newManned);
        }

        if (CollUtil.isNotEmpty(mannedList)) {
            terControlService.mannedBinding(bo);
            mannedDao.insertOrUpdateBatch(mannedList);
            deleteMannedAbnormal(mannedList);
        }
        return result;
    }

    private boolean check(String userCode, String mannedNo, Machine machine, Map<String, Manned> mannedMap, Map<Integer, Manned> machineIdMannedMap, List<MannedBindingRespVO> result) {
        int beforeSize = result.size();
        Manned machineIdManned;
        Manned manned;
        if (StrUtil.isBlank(userCode)) {
            result.add(new MannedBindingRespVO(userCode, mannedNo, "车辆编号不能为空"));
        } else if (null == machine) {
            result.add(new MannedBindingRespVO(userCode, mannedNo, "车辆不存在"));
        } else if ((machineIdManned = machineIdMannedMap.get(machine.getMachineId())) != null
                && StrUtil.isNotBlank(mannedNo)
                && (machineIdManned.getStatus().equals(MannedStatusEnum.BIND.getStatus())
                || machineIdManned.getStatus().equals(MannedStatusEnum.BINDING.getStatus()))) {
            result.add(new MannedBindingRespVO(userCode, mannedNo, "该设备已被绑定或正在绑定，请先解绑"));
        } else if (machineIdManned != null && machineIdManned.getStatus().equals(MannedStatusEnum.UNBINDING.getStatus())) {
            result.add(new MannedBindingRespVO(userCode, mannedNo, "该设备在正在解绑，请稍后再试"));
        } else if (StrUtil.isBlank(mannedNo) && machineIdManned == null) {
            result.add(new MannedBindingRespVO(userCode, mannedNo, "该设备未绑定，请先绑定再解绑"));
        } else if ((manned = mannedMap.get(mannedNo)) != null
                && StrUtil.isNotBlank(mannedNo)
                && (manned.getStatus().equals(MannedStatusEnum.BIND.getStatus())
                || manned.getStatus().equals(MannedStatusEnum.BINDING.getStatus()))) {
            result.add(new MannedBindingRespVO(userCode, mannedNo, "该载人设备已被绑定或正在绑定，请先解绑"));
        } else if (manned != null && manned.getStatus().equals(MannedStatusEnum.UNBINDING.getStatus())) {
            result.add(new MannedBindingRespVO(userCode, mannedNo, "该载人设备在正在解绑，请稍后再试"));
        }
        return beforeSize != result.size();
    }

    private Manned createManned(MannedBatchBindBO bo, Machine machine, String mannedNo, Date now, Map<Integer, Manned> machineIdMannedMap) {
        Manned manned = machineIdMannedMap.getOrDefault(machine.getMachineId(), new Manned());
        LoginInfo loginInfo = bo.getLoginInfo();
        // 绑定
        manned.setMachineId(machine.getMachineId());
        manned.setMachineNO(machine.getMachineNO());
        manned.setAccountId(bo.getAccountId());
        manned.setUpdater(loginInfo.getName());
        manned.setUpdateTime(now);
        if (StrUtil.isBlank(mannedNo)) {
            manned.setStatus(MannedStatusEnum.UNBINDING.getStatus());
        } else {
            manned.setMannedNO(mannedNo);
            manned.setCreator(loginInfo.getName());
            manned.setCreateTime(now);
            manned.setStatus(MannedStatusEnum.BINDING.getStatus());
        }
        return manned;
    }

    private void deleteMannedAbnormal(List<Manned> mannedList) {
        List<Integer> deletedMannedMachineIds = mannedList.stream()
                .filter(m -> Objects.equals(m.getStatus(), MannedStatusEnum.UNBINDING.getStatus()))
                .map(Manned::getMachineId).collect(Collectors.toList());
        List<Integer> abnormalTypes = ListUtil.of(MachineAbnormalConstant.MACHINE_ABNORMAL_MANNED, MachineAbnormalConstant.MACHINE_ABNORMAL_MANNED_LOSE);
        machineAbnormalService.batchDelete(deletedMannedMachineIds, abnormalTypes);
    }

    @Override
    public CommonExcelRespVO importExcel(MannedExcelReqVO reqVO) {
        List<MannedBindingExcelVO> mannedExcel = ExcelUtil.readAll(reqVO.getFile(), MannedBindingExcelVO.class);
        LoginInfo loginInfo = tokenService.getLoginInfo();
        MannedBatchBindBO bo = MannedBatchBindBO.of(reqVO.getAccountId(), loginInfo, mannedExcel);
        List<MannedBindingRespVO> result = batchBind(bo);
        return buildExcelRespVO(result, mannedExcel);
    }

    private CommonExcelRespVO buildExcelRespVO(List<MannedBindingRespVO> result, List<MannedBindingExcelVO> mannedExcel) {
        int failCount = result.size();
        int successCount = mannedExcel.size() - failCount;

        String url = null;
        if (failCount > 0) {
            url = String.valueOf(new ExcelUtil<>(MannedBindingRespVO.class).exportExcel(result, "操作失败设备").getData());
        }
        return new CommonExcelRespVO(successCount, failCount, url);
    }

    @Override
    public void updateIfPresent(String serNo, String paramRet) {
        if (StrUtil.isNotBlank(paramRet)
                && paramRet.toUpperCase(Locale.ROOT).contains(ControlTypeConstant.MANNED_BOUND)
                && mannedDao.countBySerNo(serNo) > 0) {
            // 绑定状态（0：未绑定，1：绑定，2：绑定中，3：绑定失败，4：解绑，5：解绑中，6：解绑失败）
            Manned manned = new Manned();
            manned.setSerNo(serNo);
            // 成功
            if ((ControlTypeConstant.MANNED_BOUND + "=" + ControlTypeConstant.CONTROL_TYPE_OK)
                    .equals(paramRet.toUpperCase(Locale.ROOT))) {
                manned.setStatus(-1);
            }
            // 失败
            else {
                manned.setStatus(1);
            }
            mannedDao.updateBySerNo(manned);
        }
    }

    @Override
    public void deleteBatchByMachineId(List<Integer> machineIds) {
        if (CollUtil.isNotEmpty(machineIds)) {
            mannedDao.deleteBatchByMachineId(StringUtil.getListString(machineIds));
        }
    }

    @Override
    public void deleteByMachineId(Integer machineId) {
        if (Objects.nonNull(machineId)) {
            mannedDao.deleteByMachineId(machineId);
        }
    }
}
